﻿/*
 * Сделано в SharpDevelop.
 * Пользователь: CrMosk
 * Дата: 05.05.2013
 * Время: 19:06
 * 
 * Для изменения этого шаблона используйте Сервис | Настройка | Кодирование | Правка стандартных заголовков.
 */
using System;
using System.Collections.Generic;
using System.Text;
namespace Region.Utils
{
  /// <summary>
  /// Description of CDate.
  /// </summary>
  class CData
  {
    const byte char_i = (byte)'i';//Int64
    const byte char_s = (byte)'s';//String
    const byte char_b = (byte)'b';//Byte[]
    const byte char_d = (byte)'d';//Dictionary
    const byte char_e = (byte)'e';
    const byte char_l = (byte)'l';//List
    const byte char_n = (byte)'n';//null
    private Object m_Value;
    public Int32 Count
    {
        get
        {
            if (m_Value is List<CData>)
                return ((List<CData>)m_Value).Count;
            else if (m_Value is Dictionary<String, CData>)
                return ((Dictionary<String, CData>)m_Value).Count;
            else return 0;
        }
    }
    public CData() { m_Value = null; }
    public CData(Int64 Value) { m_Value = Value; }
    public CData(byte[] Value) { m_Value = Value; }
    public CData(String Value) { m_Value = Value; }
    //public CData(Dictionary<byte[], CData> Value) { m_Value = Value; }
    public bool ContainsKey(Int32 Index)
    {
        if (m_Value == null) return false;
        List<CData> V = m_Value as List<CData>;
        if (V == null) return false;
        if (Index < 0) return false;
        if (Index >= V.Count) return false;
        return true;
    }
    public CData this[Int32 Index]
    {
        get
        {
            if (m_Value == null) m_Value = new List<CData>();
            List<CData> V = m_Value as List<CData>;
            if (V == null) return new CData();
            return V[Index];
        }
        set
        {
            if (m_Value == null) m_Value = new List<CData>();
            List<CData> V = m_Value as List<CData>;
            if (V == null) return;
            if (Index == V.Count) V.Add(value);
            else V[Index] = value;
        }
    }
    public bool ContainsKey(String Index)
    {
        if (m_Value == null) return false;
        Dictionary<String, CData> V = m_Value as Dictionary<String, CData>;
        if (V == null) return false;
        return V.ContainsKey(Index);
    }
    public CData this[String Index]
    {
        get
        {
            if (m_Value == null) m_Value = new Dictionary<String, CData>();
            Dictionary<String, CData> V = m_Value as Dictionary<String, CData>;
            if (V == null) return new CData();
            return V[Index];
        }
        set
        {
            if (m_Value == null) m_Value = new Dictionary<String, CData>();
            Dictionary<String, CData> V = m_Value as Dictionary<String, CData>;
            if (V == null) return;
            V[Index] = value;
        }
    }
    public static bool Write(CData Value,ref byte[] Data, Int32 Offset, out Int32 WriteSize)
    {
        if (Data == null ) Data = new byte[512];
        if (Data.Length < 512)
        {                
            byte[] NewData = new byte[512];
            Data.CopyTo(NewData, 0);
            Data = NewData;
        }
        try
        {                
            WriteSize = 0;
            if (Value.m_Value is Int64)
            {
                Data[Offset] = char_i;
                byte[] D = BitConverter.GetBytes((Int64)Value.m_Value);
                for (int i = 0; i < 8; i++) Data[Offset + 1 + i] = D[i];
                WriteSize = 9;
                return true;
            }
            else if (Value.m_Value is String)
            {
                Data[Offset] = char_s;                    
                byte[] S = Encoding.UTF8.GetBytes((String)Value.m_Value);
                Int32 Size = S.Length;                    
                byte[] D = new byte[0];
                if (Size > 0 && Size <= SByte.MaxValue) { D = new byte[] {(Byte)Size}; }
                else if (Size > SByte.MaxValue && Size <= Int16.MaxValue) { D = BitConverter.GetBytes((Int16)Size); }
                else if (Size > Int16.MaxValue && Size <= Int32.MaxValue) { D = BitConverter.GetBytes((Int32)Size); }
                else return false;
                Data[Offset + 1] = (byte)D.Length;
                D.CopyTo(Data, Offset + 2);
                S.CopyTo(Data, Offset + 2 + D.Length);
                WriteSize = 2 + D.Length + S.Length;
                return true;
            }
            else if (Value.m_Value is byte[])
            {
                Data[Offset] = char_b;
                byte[] S = (byte[])Value.m_Value;
                Int32 Size = S.Length;
                byte[] D = new byte[0];
                if (Size > 0 && Size <= SByte.MaxValue) { D = new byte[] { (Byte)Size }; }
                else if (Size > SByte.MaxValue && Size <= Int16.MaxValue) { D = BitConverter.GetBytes((Int16)Size); }
                else if (Size > Int16.MaxValue && Size <= Int32.MaxValue) { D = BitConverter.GetBytes((Int32)Size); }
                else return false;
                Data[Offset + 1] = (byte)D.Length;
                D.CopyTo(Data, Offset + 2);
                S.CopyTo(Data, Offset + 2 + D.Length);
                WriteSize = 2 + D.Length + S.Length;
                return true;
            }
            else if (Value.m_Value is Dictionary<String, CData>)
            {
                Data[Offset] = char_d;
                WriteSize = 1;
                foreach (KeyValuePair<String, CData> I in (Dictionary<String, CData>)Value.m_Value)
                {
                    Int32 KeyWS=0,ValueWS=0;
                    if (!Write(new CData(I.Key), ref Data, Offset + WriteSize, out KeyWS)) return false;
                    if (!Write(I.Value, ref Data, Offset + WriteSize + KeyWS, out ValueWS)) return false;
                    WriteSize += KeyWS + ValueWS;
                }
                Data[Offset + WriteSize] = char_e;
                WriteSize += 1;
                return true;
            }
            else if (Value.m_Value is List<CData>)
            {
                Data[Offset] = char_l;
                WriteSize = 1;
                foreach (CData I in (List<CData>)Value.m_Value)
                {
                    Int32 ValueWS = 0;                        
                    if (!Write(I, ref Data, Offset + WriteSize, out ValueWS)) return false;
                    WriteSize += ValueWS;
                }
                Data[Offset + WriteSize] = char_e;
                WriteSize += 1;
                return true;
            }
            else if (Value.m_Value == null)
            {
                Data[Offset] = char_n;
                WriteSize = 1;
                return true;
            }
                
        }
        catch (IndexOutOfRangeException) 
        {
            byte[] NewData = new byte[Data.Length*2];
            Data.CopyTo(NewData, 0);
            Data = NewData; WriteSize = 0;
            return Write(Value, ref Data, Offset, out WriteSize); 
        }
        return false;
    }
    public static CData Read(byte[] Data) { Int32 RS = 0; return CData.Read(Data, 0, out RS); }
    public static CData Read(byte[] Data, Int32 Offset, out Int32 ReadSize)
    {

        ReadSize = 0;
        switch (Data[Offset])
        {
            case char_i:
            {                    
                ReadSize = 1;
                Int64 Value = BitConverter.ToInt64(Data,Offset+ReadSize);
                ReadSize += 8;                    
                return new CData(Value);
            }
            case char_s:
            {
                Byte DL = Data[Offset + 1];
                Int32 SizeString=0;
                switch(Data[Offset + 1])
                {
                    case 0:{ SizeString = 0; ReadSize = 2;} break;
                    case 1:{ SizeString = Data[Offset + 2]; ReadSize = 3;} break;
                    case 2:{ SizeString = BitConverter.ToInt16(Data,Offset + 2); ReadSize = 4;} break;
                    case 4:{ SizeString = BitConverter.ToInt32(Data,Offset + 2); ReadSize = 6;} break;
                    default: throw new ExceptionCDataFormatError("Read:String. Ошибка чтения размера строки");
                }
                if (SizeString < 0) throw new ExceptionCDataFormatError("Read:String. Размер строки неможет быть меньше нуля");
                String S = Encoding.UTF8.GetString(Data, Offset + ReadSize, SizeString);
                ReadSize += SizeString;                    
                return new CData(S);
            }
            case char_b:
            {
                Byte DL = Data[Offset + 1];
                Int32 SizeData = 0;
                switch (Data[Offset + 1])
                {
                    case 0: { SizeData = 0; ReadSize = 2; } break;
                    case 1: { SizeData = Data[Offset + 2]; ReadSize = 3; } break;
                    case 2: { SizeData = BitConverter.ToInt16(Data, Offset + 2); ReadSize = 4; } break;
                    case 4: { SizeData = BitConverter.ToInt32(Data, Offset + 2); ReadSize = 6; } break;
                    default: throw new ExceptionCDataFormatError("Read:bytes. Ошибка чтения размера блока");
                }
                if (SizeData < 0) throw new ExceptionCDataFormatError("Read:bytes. Размер блока неможет быть меньше нуля");
                byte[] D = new byte[SizeData];
                Array.Copy(Data, Offset + ReadSize, D, 0, SizeData);
                ReadSize += SizeData;
                return new CData(D);
            }
            case char_d:
            {
                CData B = new CData();
                ReadSize = 1;
                while (Data[Offset + ReadSize] != char_e)//char e
                {
                    Int32 R1 = 0;
                    String K = (String)CData.Read(Data, Offset + ReadSize, out R1);
                    ReadSize += R1;
                    CData V = CData.Read(Data, Offset + ReadSize, out R1);
                    ReadSize += R1;
                    B[K] = V;
                }
                ReadSize += 1;
                return B;

            }
            case char_l:
            {
                CData B = new CData();
                ReadSize = 1;
                Int32 K = 0;
                while (Data[Offset + ReadSize] != char_e)//char e
                {
                    Int32 R1 = 0;
                    CData V = CData.Read(Data, Offset + ReadSize, out R1);
                    ReadSize += R1;
                    B[K] = V;
                    K += 1;
                }
                ReadSize += 1;
                return B;
            }                
        }
        return new CData();
    }        
    public static implicit operator CData(Int16 b) { return new CData(b); }
    public static implicit operator CData(Int32 b) { return new CData(b); }
    public static implicit operator CData(Int64 b) { return new CData(b); }
    public static implicit operator CData(String b) { return new CData(b); }
    public static implicit operator CData(byte[] b) { return new CData(b); }

    public static implicit operator Int16(CData b) { if (b.m_Value.GetType() == typeof(Int64))  return (Int16)(Int64)b.m_Value; else return 0; }
    public static implicit operator Int32(CData b) { if (b.m_Value.GetType() == typeof(Int64))  return (Int32)(Int64)b.m_Value; else return 0; }
    public static implicit operator Int64(CData b) { if (b.m_Value.GetType() == typeof(Int64))  return (Int64)b.m_Value; else return 0; }
    public static implicit operator String(CData b) { if (b.m_Value.GetType() == typeof(String))  return (String)b.m_Value; else return null; }
    public static implicit operator byte[](CData b) { if (b.m_Value.GetType() == typeof(byte[]))  return (byte[])b.m_Value; else return null; }
  }
  class ExceptionCDataFormatError : Exception
  {
    public ExceptionCDataFormatError(String message) : base(message) { }
  }
}
